package registnet.base;

import java.util.List;
import java.util.ListIterator;
import java.util.Vector;

import registnet.base.courseoffering.CourseOffering;
import registnet.base.source.CourseOfferingSource;
import registnet.base.source.CourseSource;

/**
 * A class that handles processes which involves a set of courses.
 */
public class CoursePeer implements ICoursePeer{

	/**
	 * The list of courses.
	 */
	private List<Course> courses;
	
	/**
	 * Default constructor.
	 */
	public CoursePeer(){
		courses = new Vector<Course>();
		load();
	}
	
	/**
	 * The result is -1 if <code>course0</code>'s rank is less than
	 * <code>course1</code>'s rank. The result is positive 1 if 
	 * <code>course0</code>'s rank is greater than <code>course1</code>'s 
	 * rank. The result is zero if both courses' ranks are equal.
	 * 
	 * @param course0
	 * @param course1
	 * @return the value 0 if the both courses' rank are equal; 
	 * a value of -1 if the <code>course0</code>'s rank is less than 
	 * <code>course1</code>'s rank; and positive 1 if <code>course0</code>
	 * has a rank greater than that of <code>course1</code>.
	 */
	private int compareByRank(Course course0, Course course1){
		if(course0.getRank() > course1.getRank())
			return 1;
		else if(course0.getRank() == course1.getRank())
			return 0;
		else
			return -1;
	}
	
	/**
	 * The result is -1 if <code>course0</code>'s class hours is less than
	 * <code>course1</code>'s class hours. The result is positive 1 if 
	 * <code>course0</code>'s class hours is greater than <code>course1</code>'s 
	 * class hours. The result is zero if both courses' class hours are equal.
	 * 
	 * @param course0
	 * @param course1
	 * @return the value 0 if the both courses' class hours are equal; 
	 * a value of -1 if the <code>course0</code>'s class hours is less than 
	 * <code>course1</code>'s class hours; and positive 1 if <code>course0</code>
	 * has a class hours greater than that of <code>course1</code>.
	 */
	private int compareByClassHours(Course course0, Course course1){
		if(course0.getClassHours() > course1.getClassHours())
			return 1;
		else if(course0.getClassHours() == course1.getClassHours())
			return 0;
		else
			return -1;
	}
	
	@Override
	public void addCourse(Course course) {
		courses.add(course);
	}

	@Override
	public void load() {
		Integer semester = (new registnet.dao.SystemPropertiesPeer().getSystemProperties().getSemester() + 1)%3;
		Integer year = new registnet.dao.SystemPropertiesPeer().getSystemProperties().getAcademicYear();
		if(semester==3) year++;
		try {
			for(registnet.dao.Course daoCourse : new registnet.dao.CoursePeer().getAllCourses()){
				//load course offerings
				List<CourseOffering> courseOfferings = new Vector<CourseOffering>();
				for(registnet.dao.CourseOffering daoCourseOffering : new registnet.dao.CourseOfferingPeer().getCourseOfferings(daoCourse.getCourseId(), semester, year)){
					CourseOffering courseOffering = new CourseOfferingSource(daoCourseOffering.getCourseOfferingId()).getCourseOffering();
					if(courseOffering.getLectureTime()==null && courseOffering.getLaboratoryTime()==null && courseOffering.getRecitationTime()==null)
						continue;
					courseOffering.loadSchedule();
					if(!courseOffering.getSchedule().hasTask())	//ignore course offerings that has no tasks
						continue;
					courseOffering.setSlots(courseOffering.getStudentCount());
					courseOfferings.add(courseOffering);
				}
				if(courseOfferings.size() == 0)	//ignore courses which are not offered for the semester
					continue;
				
				Course course = new CourseSource(daoCourse.getCourseCode()).getCourse();
				course.setClassHours(daoCourse.getLectureHoursPerWeek()+daoCourse.getLaboratoryHoursPerWeek());
				if(course.getClassHours()==0)	//ignore courses with 0 class hours
					continue;
				course.setCourseOfferings(courseOfferings);
				
				addCourse(course);
			}
		} catch (Exception e) {
			System.err.println("****[error] Courses loading failed.");
		}
	}

	@Override
	public void rankCourses() {
		ListIterator<Course> li = courses.listIterator();
		while(li.hasNext()){
			Course course = li.next();
			course.computeRank();
		}
	}

	@Override
	public void sortCoursesByRank() {
		List<Course> retval = courses;
		int n = retval.size();
		for(int i=0; i<n-1; i++){
			for(int j=0; j<(n-1-i); j++){
				Course a = retval.get(j);
				Course b = retval.get(j+1);
				if(compareByRank(a,b)>0){
					retval.set(j,b);
					retval.set(j+1,a);
				}
			}
		}
		courses = retval;
	}
	
	/**
	 * Sorts the list of courses in ascending order based
	 * on the course's class hours.
	 */
	public void sortCoursesByClassHours(){
		List<Course> retval = courses;
		int n = retval.size();
		for(int i=0; i<n-1; i++){
			for(int j=0; j<(n-1-i); j++){
				Course a = retval.get(j);
				Course b = retval.get(j+1);
				if(compareByClassHours(a,b)<0){
					retval.set(j,b);
					retval.set(j+1,a);
				}
			}
		}
		courses = retval;
	}

	/**
	 * Returns the list of courses.
	 * @return the list of courses.
	 */
	public List<Course> getCourses() {
		return courses;
	}
	
}